/* *
 * @file    app_rgb.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   WS2812
 * @version 0.1
 * @date    2022-09-19
 * 
 * @copyright Copyright (c) 2022
 * */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/cdefs.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt.h"
#include "sdkconfig.h"
#include "esp_log.h"
 
#include "app_rgb.h" 

#if 1 
 
// static const char *TAG = "app_rgb";
 
#define RMT_TX_CHANNEL         RMT_CHANNEL_0    
#define RMT_TX_GPIO            35

#define LED_MAX_NUM            6   // LED个数

/**********************************************************************
24bit 数据结构
G7 G6 G5 G4 G3 G2 G1 G0 R7 R6 R5 R4 R3 R2 R1 R0 B7 B6 B5 B4 B3 B2 B1 B0
*注：高位先发，按照 GRB 的顺序发送数据。
***********************************************************************/

// T0H 0 code, high voltage time        220ns~380ns
// T1H 1 code, high voltage time        580ns~1µs
// T0L 0 code, low voltage time         580ns~1µs
// T1L 1 code, low voltage time         580ns~1µs
// RES Frame unit, low voltage time     >280µs

#define WS2812_T0H_NS       (3)     // 300ns; 时钟是10MHz
#define WS2812_T0L_NS       (7)     
#define WS2812_T1H_NS       (7)
#define WS2812_T1L_NS       (3)
#define WS2812_RESET_US     (300)   

typedef struct {
    uint8_t buffer[LED_MAX_NUM * 3];  // GREEN -> RED -> BULE
    rmt_channel_t channel;
    uint8_t strip_num;   /*!< Maximum LEDs in a single strip */
} ws2812_t;

static ws2812_t ws2812 = { .strip_num = LED_MAX_NUM, .channel = RMT_TX_CHANNEL, };
 
static hsv_t hsv = { 0 };

/**
 * @brief Simple helper function, converting HSV color space to RGB color space
 *
 * Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV
 *
 */
static void led_strip_hsv2rgb(hsv_t hsv, rgb_t *rgb)
{
    hsv.hue %= 360; // h -> [0,360]
    uint16_t rgb_max = hsv.value * 2.55f;
    uint16_t rgb_min = rgb_max * (100 - hsv.saturation) / 100;
 
    // RGB adjustment amount by hue
    uint16_t rgb_adj = (rgb_max - rgb_min) * (hsv.hue % 60) / 60;

    switch (hsv.hue / 60) {
    case 0:
        rgb->red   = rgb_max;
        rgb->green = rgb_min + rgb_adj;
        rgb->blue  = rgb_min;
        break;
    case 1:
        rgb->red   = rgb_max - rgb_adj;
        rgb->green = rgb_max;
        rgb->blue  = rgb_min;
        break;
    case 2:
        rgb->red   = rgb_min;
        rgb->green = rgb_max;
        rgb->blue  = rgb_min + rgb_adj;
        break;
    case 3:
        rgb->red   = rgb_min;
        rgb->green = rgb_max - rgb_adj;
        rgb->blue  = rgb_max;
        break;
    case 4:
        rgb->red   = rgb_min + rgb_adj;
        rgb->green = rgb_min;
        rgb->blue  = rgb_max;
        break;
    default:
        rgb->red   = rgb_max;
        rgb->green = rgb_min;
        rgb->blue  = rgb_max - rgb_adj;
        break;
    }
}
 
/**
 * @brief Conver RGB data to RMT format.
 *
 * @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
 *
 * @param[in] src: source data, to converted to RMT format
 * @param[in] dest: place where to store the convert result
 * @param[in] src_size: size of source data
 * @param[in] wanted_num: number of RMT items that want to get
 * @param[out] translated_size: number of source data that got converted
 * @param[out] item_num: number of RMT items which are converted from source data
 */
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
        size_t wanted_num, size_t *translated_size, size_t *item_num)
{
    if (src == NULL || dest == NULL) {
        *translated_size = 0;
        *item_num = 0;
        return;
    }

    const rmt_item32_t bit0 = {{{ WS2812_T0H_NS, 1, WS2812_T0L_NS, 0 }}}; // Logical 0
    const rmt_item32_t bit1 = {{{ WS2812_T1H_NS, 1, WS2812_T1L_NS, 0 }}}; // Logical 1

    size_t size = 0;
    size_t num = 0;
    uint8_t *psrc = (uint8_t *)src;
    rmt_item32_t *pdest = dest;
    while (size < src_size && num < wanted_num) {
        for (int i = 0; i < 8; i++) {
            // MSB first
            if (*psrc & (1 << (7 - i))) {
                pdest->val =  bit1.val;
            } else {
                pdest->val =  bit0.val;
            }
            num++;
            pdest++;
        }
        size++;
        psrc++;
    }
    *translated_size = size;
    *item_num = num;
}
 
 
esp_err_t ws2812_rgb_refresh(uint8_t red, uint8_t green, uint8_t blue)
{
    for (uint8_t i = 0; i < ws2812.strip_num * 3; i += 3) {
        // In thr order of GRB
        ws2812.buffer[i + 0] = green;   // GREEN
        ws2812.buffer[i + 1] = red;     // RED
        ws2812.buffer[i + 2] = blue;    // BLUE
    }
    rmt_write_sample(ws2812.channel, ws2812.buffer, ws2812.strip_num * 3, true);         
    return rmt_wait_tx_done(ws2812.channel, pdMS_TO_TICKS(100));
}



// HSV 色彩模型（Hue 色度, Saturation 饱和度, Value纯度,亮度）
void app_rgb_set_hsv(hsv_t hsv)
{
    // if (hsv.hue != hue || hsv.saturation != saturation || hsv.value != value) {
        
    // } else {
    //     return;
    // }
 
    // Build RGB values
    rgb_t rgb = { 0 };
    led_strip_hsv2rgb(hsv, &rgb);

    ws2812_rgb_refresh(rgb.red, rgb.green, rgb.blue);
}

void app_rgb_set_switch(bool onoff)
{
    if (onoff == true) {  // on
        hsv.hue = 0;         // 0：红色
        hsv.saturation = 100;  // 0: 白色
        hsv.value = 50;      // 亮度：50%
    } else {  // off
        hsv.hue = 0;         // 0：红色
        hsv.saturation = 0;  // 0: 白色
        hsv.value = 0;       // 0: 黑色
    }
    app_rgb_set_hsv(hsv);
}

void app_rgb_set_lightness(uint8_t lightness)
{
    hsv.value = lightness;
    app_rgb_set_hsv(hsv);
}

void app_rgb_init(void)
{
    rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO, RMT_TX_CHANNEL);
    // set counter clock to 10MHz
    rmt_tx_config.clk_div = 8;   // RMT时钟源是80MHz
    // rmt_tx_config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;

    ESP_ERROR_CHECK(rmt_config(&rmt_tx_config));
    ESP_ERROR_CHECK(rmt_driver_install(rmt_tx_config.channel, 0, 0));

    // set ws2812 to rmt adapter
    rmt_translator_init(rmt_tx_config.channel, ws2812_rmt_adapter);  
 
#if 0  // test...
    while (1) {
        // static bool onoff = 1;
        // app_rgb_set_switch(onoff);   onoff = !onoff;
        hsv.saturation = 100;
        hsv.value = 30;
        hsv.hue += 30; 
        app_rgb_set_hsv(hsv);
        // ws2812_rgb_refresh(0, 0, 255);
        printf("hsv.hue = %d\n", hsv.hue);
        vTaskDelay(900);
    }
#endif    
}

#endif

